/*
 * Copyright (C) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "accessibility_espeak_player.h"
#include "hilog_wrapper.h"

#ifndef ESPEAK_TAG
#define ESPEAK_TAG "ESPEAK_TAG| "
#endif

namespace OHOS {
namespace Accessibility {

EspeakPlayer::EspeakPlayer()
{
    Init();
}

EspeakPlayer::~EspeakPlayer()
{
    Release();
}

bool EspeakPlayer::Init()
{
    HILOG_DEBUG(ESPEAK_TAG);
    AudioStandard::AudioStreamType streamType = AudioStandard::AudioStreamType::STREAM_ACCESSIBILITY;
    renderer_ = AudioStandard::AudioRenderer::Create(streamType);
    if(renderer_ == nullptr){
        HILOG_ERROR(ESPEAK_TAG "Init faied, audioRenderer is nullptr");
    }
    supportedSampleList_=AudioStandard::AudioRenderer::GetSupportedSamplingRates();
    supportedChannelsList_=AudioStandard::AudioRenderer::GetSupportedChannels();
    if(supportedSampleList_.empty() || supportedChannelsList_.empty()){
        HILOG_ERROR(ESPEAK_TAG "supportedSampleList or supportedChannelsList is empty");
    }

    // 初始化buffer
    sampleBuffer_ = std::make_unique<uint8_t[]>(SAMPLE_BUFFER_CAPACITY);
    // 启动播放线程
    playThread_ = std::thread(&EspeakPlayer::PlayBackThread, this);

    HILOG_DEBUG(ESPEAK_TAG "Init success");
    return true;
}

bool EspeakPlayer::Start()
{
    HILOG_DEBUG(ESPEAK_TAG);
    if(!renderer_){
        HILOG_ERROR(ESPEAK_TAG "Start failed, audioRenderer is nullptr");
        return false;
    }
    if(!renderer_->Start()){
        HILOG_DEBUG(ESPEAK_TAG "Start failed");
        return false;
    }
    
    HILOG_DEBUG(ESPEAK_TAG "Start success");
    return true;
}

bool EspeakPlayer::Pause()
{
    HILOG_DEBUG(ESPEAK_TAG);
    if(!renderer_){
        HILOG_ERROR(ESPEAK_TAG "Pause failed, audioRenderer is nullptr");
        return false;
    }
    if(!renderer_->Pause()){
        HILOG_DEBUG(ESPEAK_TAG "Pause failed");
        return false;
    }
    
    HILOG_DEBUG(ESPEAK_TAG "Pause success");
    return true;
}

bool EspeakPlayer::Stop()
{
    HILOG_DEBUG(ESPEAK_TAG);
    if(!renderer_){
        HILOG_ERROR(ESPEAK_TAG "Stop failed, audioRenderer is nullptr");
        return false;
    }
    if(!renderer_->Stop()){
        HILOG_DEBUG(ESPEAK_TAG "Stop failed");
        return false;
    }
    
    HILOG_DEBUG(ESPEAK_TAG "Stop success");
    return true;
}
 
bool EspeakPlayer::Drain()
{
    HILOG_DEBUG(ESPEAK_TAG);
    if(!renderer_){
        HILOG_ERROR(ESPEAK_TAG "Drain failed, audioRenderer is nullptr");
        return false;
    }
    if(!renderer_->Drain()){
        HILOG_DEBUG(ESPEAK_TAG "Drain failed");
        return false;
    }
    HILOG_DEBUG(ESPEAK_TAG "Drain success");
    return true;
}

bool EspeakPlayer::Flush()
{
    HILOG_DEBUG(ESPEAK_TAG);
    if(!renderer_){
        HILOG_ERROR(ESPEAK_TAG "Flush failed, audioRenderer is nullptr");
        return false;
    }
    if(!renderer_->Flush()){
        HILOG_DEBUG(ESPEAK_TAG "Flush failed");
        return false;
    }
    HILOG_DEBUG(ESPEAK_TAG "Flush success");
    return true;
}

bool EspeakPlayer::Release()
{
    HILOG_DEBUG(ESPEAK_TAG);
    //先释放播放线程资源
    exitFlag_ = true;
    cond_.notify_all();
    if(playThread_.joinable()){
        playThread_.join();
    }
    if(renderer_){
        renderer_->Release();
        renderer_ = nullptr;
    }
    return true;
}

bool EspeakPlayer::SetParams(int32_t channels, int32_t sampleRate)
{
    HILOG_DEBUG(ESPEAK_TAG "channels:%{public}d, sampleRate:%{public}d", channels, sampleRate);
    if(!renderer_){
        HILOG_ERROR(ESPEAK_TAG "SetParams failed, audioRenderer is nullptr");
        return false;
    }
    AudioStandard::AudioRendererParams rendererParams;
    bool flag1=false, flag2=false; // 判断是否支持
    for(auto &c : supportedChannelsList_){
        if(c == channels){
            rendererParams.channelCount = c;
            flag1 = true;
            break;
        }
    }
    //尽可能刚好覆盖数据的采样率
    for(auto &s : supportedSampleList_){
        if(sampleRate<=s){
            rendererParams.sampleRate=s;
            flag2 = true;
            break;
        }
    }
    if(!flag1 || !flag2){
        HILOG_ERROR(ESPEAK_TAG "SetParams failed, channels:%{public}d, sampleRate:%{public}d", channels, sampleRate);
        return false;
    }
    HILOG_DEBUG(ESPEAK_TAG "find supported channels:%{public}d, sampleRate:%{public}d", rendererParams.channelCount, rendererParams.sampleRate);
    rendererParams.sampleFormat = AudioStandard::AudioSampleFormat::SAMPLE_S16LE;
    rendererParams.encodingType = AudioStandard::AudioEncodingType::ENCODING_PCM;
    if(renderer_->SetParams(rendererParams)!=0){
        HILOG_ERROR(ESPEAK_TAG "SetParams failed");
        return false;
    }
    HILOG_DEBUG(ESPEAK_TAG "SetParams success");
    return true;
}

bool EspeakPlayer::WriteToBuffer(short *wav, int numsamples)
{
    int sampleSize = numsamples * sizeof(short);
    HILOG_DEBUG(ESPEAK_TAG "numsamples:%{public}d, sampleSize:%{public}d", numsamples, sampleSize);
    if(numsamples <= 0){
        return false;
    }

    std::unique_lock<std::mutex> lock(mutex_);
    // 说明当前没有播放，
    if (!bufferFlag_.load())
    {
        bufferSize_ = 0;
        bufferWritePos_ = 0;
        bufferFlag_ = true;
    }else{
        HILOG_DEBUG(ESPEAK_TAG "currently buffer is not empty");
    }
    size_t bufferSizeCP = bufferSize_.load();
    HILOG_DEBUG(ESPEAK_TAG "start to write to buffer, current buffer size:%{public}d", bufferSizeCP);
    // 填充buffer
    if (bufferSizeCP+sampleSize > SAMPLE_BUFFER_CAPACITY)
    {
        HILOG_ERROR(ESPEAK_TAG "bufferSize_+sampleSize > SAMPLE_BUFFER_CAPACITY");
        return false;
    }
    //将wav数据追加到sampleBuffer_中
    memcpy(sampleBuffer_.get()+bufferSizeCP, static_cast<uint8_t *>(static_cast<void *>(wav)), sampleSize);
    bufferSize_.store(bufferSizeCP+sampleSize);
    cond_.notify_all();

    HILOG_DEBUG(ESPEAK_TAG "write to buffer success");
    return true;
}

// 未加锁，只在PlayBackThread中调用
bool EspeakPlayer::WriteFromBuffer(){
    size_t bytesToWrite = bufferSize_.load(); //是为了减少对bufferSize_的访问次数
    size_t bytesWritten = bufferWritePos_.load();
    HILOG_DEBUG(ESPEAK_TAG "start to write from buffer, bytesToWrite:%{public}zu, bufferWritePos_:%{public}zu", bytesToWrite, bytesWritten);
    // 分多次写入样本数据，单词写入4096bytes，目的是让播放线程能够及时响应中断信号
    while(bytesWritten < bytesToWrite){
        if(bytesWritten + SINGLE_WRITE_BYTES <= bytesToWrite){
            bytesWritten += renderer_->Write(sampleBuffer_.get() + bytesWritten, SINGLE_WRITE_BYTES);
        }else{
            bytesWritten += renderer_->Write(sampleBuffer_.get() + bytesWritten, bytesToWrite - bytesWritten);
        }
        HILOG_DEBUG(ESPEAK_TAG "%{public}zu bytes have been written", bytesWritten);
        if (bytesWritten < 0 || !bufferFlag_|| exitFlag_) break;
    }
    
    bufferWritePos_ = bytesWritten;
    HILOG_DEBUG(ESPEAK_TAG "write over, bytesWritten: %{public}zu, bytesToWrite: %{public}zu", bytesWritten, bytesToWrite);
    return true;
}

bool EspeakPlayer::WriteFlag()
{
    return bufferFlag_.load()&&(bufferSize_.load()>bufferWritePos_.load());
}

void EspeakPlayer::PlayBackThread()
{
    while(!exitFlag_.load()){
        std::unique_lock<std::mutex> lock(mutex_);
        if(!WriteFlag()){
            cond_.wait(lock, [this](){return WriteFlag() || exitFlag_.load();}); // 等待buffer填充信号或退出信号
        }
        if(exitFlag_.load()) break;
        HILOG_DEBUG(ESPEAK_TAG "PlayBackThread start to play");
        if(!renderer_->Start()){
            HILOG_ERROR(ESPEAK_TAG "PlayBackThread Start failed");
            continue;
        }
        // 可以在这加一层while
        WriteFromBuffer();
        // 这有问题，一次合成会分多次写入，写完一次不能直接flush和stop
        if(!renderer_->Flush()||!renderer_->Stop()){
            HILOG_ERROR(ESPEAK_TAG "PlayBackThread Flush or Stop failed");
        }
        HILOG_DEBUG(ESPEAK_TAG "PlayBackThread end to play");
    }
    HILOG_DEBUG(ESPEAK_TAG "PlayBackThread exit");
}

bool EspeakPlayer::Play(short *wav, int numsamples)
{
    HILOG_DEBUG(ESPEAK_TAG "numsamples: %{public}d", numsamples);
    if (wav == nullptr) {
        HILOG_ERROR(ESPEAK_TAG "wave is nullptr");
        return false;
    }
    if (numsamples <= 0) {
        HILOG_DEBUG(ESPEAK_TAG "numsamples is 0");
        return false;
    }
    WriteToBuffer(wav, numsamples);

    HILOG_DEBUG(ESPEAK_TAG "Play success");
    return true;
}

void EspeakPlayer::Interrupt()
{
    HILOG_DEBUG(ESPEAK_TAG "Interrupt");
    bufferFlag_.store(false);
}

} // namespace Accessibility
} // namespace OHOS